控制反转(IoC)

1. IoC的原型

以前写ServiceImpl层调用DaoImpl层,代码如下

package org.gs.service;

import org.gs.dao.UserDao;
import org.gs.dao.UserDaoImpl;

/**
 * @author admin
 * @date 2021/9/12 2:21 下午
 */
public class UserServiceImpl implements UserService{

    private UserDao userDao = new UserDaoImpl();

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

其中需要调用到UserDao的实现类代码

private UserDao userDao = new UserDaoImpl();

在程序中是固定的,程序主动创建对象,如要调换其他实现类,则牵一发而动全身。

解决方法:

使用setter等方法(但是在Spring中只能使用setter方法进行对象注入,所以必须要有对应属性的公共权限的setter方法)动态放入实现类,将控制权交给其他人。

private UserDao userDao;

public UserServiceImpl(UserDao userDao) {
  this.userDao = userDao;
}

在以上代码中,程序不再具有主动性,而变成了被动的接收对象。由用户来选择调用什么实现类。

这种思想从本质上解决了问题,程序员不用再去管理对象的创建,同时系统的耦合性大大降低。可以更加专注在业务的实现方面。这就是IoC的原型。

2. IoC的本质

IoC是一种设计思想,DI(依赖注入)是实现IoC的一种方法。

控制反转就是获得依赖对象的控制权反转了。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

container magic
Image

3. 开始

  1. 准备的dao和service类如下

    package org.gs.dao;
    
    /**
     * @author admin
     * @date 2021/9/12 2:20 下午
     */
    public class UserDaoImpl implements UserDao{
        @Override
        public void getUser() {
            System.out.println("默认获取用户的数据");
        }
    }
    
    package org.gs.service;
    
    import org.gs.dao.UserDao;
    
    /**
     * @author admin
     * @date 2021/9/12 2:21 下午
     */
    public class UserServiceImpl implements UserService{
    
        private UserDao userDao;
    
        // Spring通过setter方法进行依赖注入,所以必须要有公共权限的setter方法
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void getUser() {
            userDao.getUser();
        }
    }
    
  2. 在res资源目录下创建beans.xml配置文件。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean class="org.gs.dao.UserDaoImpl" name="userDao" />
        <bean class="org.gs.service.UserServiceImpl" name="userService">
            <!--name:要注入对象的属性名 ref:引用Spring管理的bean进行注入-->
            <property name="userDao" ref="userDao"/>
            <!--value:非引用类型的注入-->
            <!--<property name="" value=""/>-->
        </bean>
    
    </beans>
    
  3. 测试

    @Test
    public void Test() {
      ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
      UserService userService = (UserService) context.getBean("userService");
      // 或者通过反射指定对象的类型,避免强转
      // UserService userService = context.getBean("userService", UserService.class);
      userService.getUser();
    }
    

通过上述的代码可以发现,xml中配置的对象以及对象中的属性都是由Spring创建、设置和管理的,这个过程就是控制反转。将控制权交给Spring,由Spring进行对象的管理。程序本身不创建对象,而编程被动的接收对象。

值得注意的是,Spring中的参数注入是使用set方法进行注入的,所以需要有公共权限的setter方法

在Spring中配置的对象,可以通过xml文件进行管理,而基本不需要动Java代码,实现解耦。

4. IoC创建对象的方式

注意点:

  1. Spring在配置文件加载的时候,容器中管理的对象就已经被初始化了,非懒加载。

  2. Spring中管理的对象,默认是单例模式。也就是说get同一个对象的时候拿到的是同一个实例。

  3. Spring默认使用无参构造器创建对象

  4. 假设要使用有参构造器创建对象,有以下方法:

    1. 如果bean定义的构造函数参数中不存在潜在的歧义,则bean定义中构造函数参数的顺序是bean实例化时将这些参数提供给适当构造函数的顺序。

      package x.y;
      
      public class ThingOne {
      
          public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
              // ...
          }
      }
      
      <beans>
          <bean id="beanOne" class="x.y.ThingOne">
              <constructor-arg ref="beanTwo"/>
              <constructor-arg ref="beanThree"/>
          </bean>
      
          <bean id="beanTwo" class="x.y.ThingTwo"/>
      
          <bean id="beanThree" class="x.y.ThingThree"/>
      </beans>
      
    2. 通过下标赋值(从0开始)

      <bean id="exampleBean" class="examples.ExampleBean">
          <constructor-arg index="0" value="7500000"/>
          <constructor-arg index="1" value="42"/>
      </bean>
      
    3. 通过类型赋值

      <bean id="exampleBean" class="examples.ExampleBean">
          <constructor-arg type="int" value="7500000"/>
          <constructor-arg type="java.lang.String" value="42"/>
      </bean>
      
    4. 通过属性名赋值

      <bean id="exampleBean" class="examples.ExampleBean">
          <constructor-arg name="years" value="7500000"/>
          <constructor-arg name="ultimateAnswer" value="42"/>
      </bean>
      
Copyright © rootwhois.cn 2021-2022 all right reserved,powered by GitbookFile Modify: 2023-03-05 10:55:52

results matching ""

    No results matching ""